import { Transaction, TransactionReceipt } from "web3-core/types";
import { TransactionMessage } from '../../../Interface/TransactionMessage';
import { post } from 'request';
import { logger } from '../../../models/LogModel';
import { EthBlock, EthUserAddress, EthTransaction, EthCoinType, EthTransfer, EthConfig } from '../../../database/eth/EthDatabase';
import { TopUp } from '../../../abstract/abstract';
import Web3 from 'web3';
import { fromWei, toHex } from 'web3-utils';
import { ethNodeUrl } from '../../../global';
import dec from 'decimal.js';
import Tx = require('ethereumjs-tx');

class EthHandle implements TopUp { 
  
  private transaction: Transaction;
  private blockNum: number;
  private transactionMessage: TransactionMessage;
  private transferAddress: string;
  static web3: Web3 = new Web3(ethNodeUrl);
  /**
   * Creates an instance of EthHandle.
   * @param {Transaction} transaction
   * @param {number} blockNum
   * @memberof EthHandle
   */
  constructor(transaction: Transaction, blockNum: number) {
    this.transaction = transaction;
    this.blockNum = blockNum;
    this.transferAddress = transaction.to;
    this.buildTransactionMessage();
  }
  
  /**
   * 构建交易消息
   * @memberof EthHandle
   */
  buildTransactionMessage() {
    this.transactionMessage = {
      blockNum: this.blockNum,
      hash: this.transaction.hash,
      from: this.transaction.from,
      to: this.transaction.to,
      amount: fromWei(this.transaction.value.toString()),
    }
  }

  /**
   * 发送交易通知到平台
   * @param {number} [num=1]
   * @memberof EthHandle
   */
  async sendTransactionMessage(num: number = 1) {
    const sendUrl = await EthConfig.getParam('top_up_callback_url');
    if(sendUrl) {
      post(sendUrl, {
        form: this.transactionMessage
      }, (error, res, body) => {
        if(num < 5){
          if(error) {
            logger('ethHandle').debug(`eth 交易信息发送失败,检测到致命BUG。 ${num}`);
          }else{
            if(body === 'success') {
              logger('ethHandle').info(`eth 交易信息发送成功---${this.transactionMessage.hash} ${num}`);
            }else {
              logger('ethHandle').info(`eth 交易信息发送失败---${this.transactionMessage.hash} ${num}`);
              setTimeout(() => {
                this.sendTransactionMessage(num+=1)
              }, 5000)
            }
          }
        }
      });
    }
  }

  /**
   * 确定区块
   * @memberof EthHandle
   */
  async affertBlock() {
    logger('ethHandle').trace(`处理hash ${this.transaction.hash}`);
    let coinTypeId = await EthCoinType.getCoinTypeId('eth');
    const save = await EthTransaction.save(
      this.transactionMessage.hash, 
      this.blockNum, 
      this.transactionMessage.from, 
      this.transactionMessage.to, 
      this.transactionMessage.amount, 
      coinTypeId);
    if(save){
      await EthBlock.captureNumAdd(this.blockNum);
      await EthCoinType.coinAmountIncrease(coinTypeId, this.transactionMessage.amount);
    }
  }

  /**
   * 转移ETH
   * @memberof EthHandle
   */
  async transfer() {
    logger('ethHandle').trace(`
      转移该笔交易ETH hash ${this.transaction.hash}
      address ${this.transferAddress}
    `);
    const address = await EthConfig.getParam('reap_address');
    const ethAmount = await EthHandle.web3.eth.getBalance(this.transferAddress);
    const gasLimit = await EthHandle.web3.eth.estimateGas({to: address});
    const gasPrice = await EthHandle.web3.eth.getGasPrice();
    const number = await EthHandle.web3.eth.getTransactionCount(this.transferAddress);
    const gas = new dec(gasLimit).mul(gasPrice);
    const sendEthAmount = new dec(ethAmount).sub(gas);
    if(sendEthAmount.gt(0)){
      const rawTx = {
        nonce: toHex(number),
        gasPrice: toHex(gasPrice),
        gasLimit: toHex(gasLimit),
        to: address,
        value: toHex(sendEthAmount.toString())
      }
  
      const privateKey = await this.getAddressPrivateKey();
  
      const tx = new Tx(rawTx);
  
      tx.sign(Buffer.from(privateKey, 'hex'));
  
      const serializedTx = tx.serialize();
  
      EthHandle.web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex'))
      .on('transactionHash', hash => {
        logger('ethHandle').trace(`
          转移ETH hash ${hash}
          地址 ${this.transferAddress}
        `);
      })
      .on('receipt', async receipt => {
        const sendAmount = sendEthAmount.div(10**18);
        logger('ethHandle').trace(`
          转移ETH hash ${receipt.transactionHash}  
          地址 ${this.transferAddress}
          数量 ${sendAmount}
          成功
        `);

        const transaction = this.buildTransferTransaction(receipt, sendAmount.toString());
        const coinTypeId = await EthCoinType.getCoinTypeId('eth');
        EthTransfer.save(1, coinTypeId, transaction, '转移eth到总账号');
      })
    }else{
      logger('ethHandle').trace(`
        该地址ETH不足以转账 
        地址 ${this.transferAddress}
      `);
    }
  }

  /**
   * 构建转移交易
   * @param {TransactionReceipt} receipt
   * @param {(string|number)} amount
   * @returns
   * @memberof EthHandle
   */
  buildTransferTransaction(receipt: TransactionReceipt, amount: string|number) {
    const transferTransaction: TransactionMessage = {
      blockNum: receipt.blockNumber,
      hash: receipt.transactionHash,
      from: receipt.from,
      to: receipt.to,
      amount: amount
    }
    return transferTransaction;
  }

  /**
   * 获取以太坊地址私钥
   * @returns
   * @memberof EthHandle
   */
  async getAddressPrivateKey() {
    const address = await EthUserAddress.getUserAddressPrivateKey(this.transferAddress);
    ;
    return address.split('0x')[1];
  }
}

export default EthHandle;
